/*  Print Out a Procedure */

/*  Copyright (c) 1994 Stanford University

    All rights reserved.

    This software is provided under the terms described in
    the "suif_copyright.h" include file. */

/* 
	modified by Khaled Mohammed ( Student # 992491362 )

*/

#include "cfg.h"

#define PRINT_INSTRS       	0 
#define PRINT_DOMINATORS   	0 
#define ASSIGNMENT1        	0 
#define ASSIGNMENT2         0
#define ASSIGNMENT3        	0 
#define ASSIGNMENT4			0
#define ASSIGNMENT5			1

/* freeing memory 
	- remvoing all the nodes from the graph
	- and freeing them 
*/
void free_cfg(basic_block_node * cfg) {
	basic_block_node * current_block = cfg;

	while(current_block) {
		basic_block_node * tmpblock = current_block;
		instr_node * current_instr = current_block->start;
		block_ptr * successor = current_block->successor;
		block_ptr * predecessor = current_block->predecessor;
		
		while(current_instr) {
			instr_node * tmp = current_instr;
			current_instr = current_instr->next;
			tmp->instr = NULL;
			tmp->previous = NULL;
			tmp->next = NULL;
			free(tmp);
		}
		while(successor) {
			block_ptr * tmp = successor;
			successor = successor->next;
			tmp->block = NULL;
			tmp->next = NULL;
			free(tmp);
		}
		while(predecessor) {
			block_ptr * tmp = predecessor;
			predecessor = predecessor->next;
			tmp->next = NULL;
			tmp->block = NULL;
			free(tmp);
		}
	
		free_bit_vector(current_block->dom);
		//free_bit_vector(current_block->idom);

		current_block = current_block->next;
		free(tmpblock);
	}
}


simple_instr ** headofinstr;

/*  Read through the list of instructions and print them out.  */

simple_instr *
do_procedure (simple_instr *inlist, char *proc_name)
{
	int blockCount;
	int instrCount;
	instr_node * instr_head = NULL;
	basic_block_node * basic_block_head = NULL;
	loop_block * loop_head = NULL;
	instr_node * preheaders = NULL;
	basic_block_node * current_block = NULL;
	int loop_count = 0;
	reg * reg_list = NULL;
	int totalregs = 0;
	expression * expr_list = NULL;
	int totalexprs = 0;

    // assignment 5 variable declarations
   
    headofinstr = &inlist;
	// number the instructions
	instrCount = number_instrs(&instr_head, inlist);	

	// find the basic blocks
	blockCount = find_basic_block (&basic_block_head, instr_head);
   // find the predecessors
	find_predecessors(basic_block_head);

#if PRINT_INSTRS
	print_blocks(basic_block_head);
#endif

 

#if ASSIGNMENT1
	// print the cfg
	printf("cfg %s %d", proc_name, blockCount);
	print_cfg(basic_block_head, blockCount);
//#endif

	// find dominators
	find_dominators(basic_block_head, blockCount);

#if PRINT_DOMINATORS
	print_dom(basic_block_head, blockCount);
	printf("\n");
#endif

	// find proper and immediate dominators
	find_idominators(basic_block_head, blockCount);
//#if ASSIGNMENT1
	// print the immediate dominators
	print_idom(basic_block_head, proc_name, blockCount);
	printf("\n");
#endif

#if ASSIGNMENT2
    find_dominators(basic_block_head, blockCount);
	// find loops and insert label or jmp instructions appropriately
	find_loops(basic_block_head, &inlist, &preheaders);

	// free the memory
	free_cfg(basic_block_head);
	instr_head = NULL;
	basic_block_head = NULL;

    // re-number the instructions
	number_instrs(&instr_head, inlist);
#if PRINT_INSTRS
	print_instrs(instr_head);
#endif

    // re-calculate the basic blocks
	blockCount = find_basic_block (&basic_block_head, instr_head);
	find_predecessors(basic_block_head);

	find_dominators(basic_block_head, blockCount);

	printf("loopcfg %s %d", proc_name, blockCount);
	print_cfg(basic_block_head, blockCount);
	printf("\n");

	loop_count = find_loop_info(basic_block_head, &loop_head, blockCount);

	printf("natural_loops %s %d\n", proc_name, loop_count );
	print_natural_loops(loop_head, blockCount);
#endif

#if ASSIGNMENT3
    totalregs = find_variables(instr_head, &reg_list);    
    print_variables(reg_list, proc_name, totalregs);
    lv_init(basic_block_head, totalregs, reg_list);
    print_def_set(basic_block_head, proc_name, blockCount);    
    print_use_set(basic_block_head, proc_name, blockCount);
    print_lvout(basic_block_head, proc_name, blockCount);
#endif

#if ASSIGNMENT4
	totalexprs = find_expressions(instr_head, &expr_list);
	print_expressions(expr_list, proc_name, totalexprs);
	expr_init(basic_block_head, totalexprs, expr_list);
	print_eval_expr(basic_block_head, proc_name, blockCount);
	print_kill_expr(basic_block_head, proc_name, blockCount);
	print_aeout(basic_block_head, proc_name, blockCount);
#endif

#if ASSIGNMENT5	
    printf("\n\n\t%s\n\n", proc_name);
    if(!getenv("NO_CP"))    
    {
        int rerun = 0;
        do {
            do_copy_propagation(&basic_block_head, &instr_head, inlist, &blockCount, &instrCount);
            rerun = do_constant_folding(basic_block_head);
            do_dead_code_elimination(basic_block_head, instr_head, instrCount, blockCount);
        } while(rerun);
    } else if(!getenv("NO_OPTIONAL") )
        do_dead_code_elimination(basic_block_head, instr_head, instrCount, blockCount);
    
    if(!getenv("NO_LOOP"))
    {
        int rerun = 0;
        
        instr_head = NULL;
            basic_block_head = NULL;
            preheaders = NULL;
            // re-number the instructions
            number_instrs(&instr_head, inlist);

            // re-calculate the basic blocks
            blockCount = find_basic_block (&basic_block_head, instr_head);
            find_predecessors(basic_block_head);

            find_dominators(basic_block_head, blockCount);
            // find loops and insert label or jmp instructions appropriately
            find_loops(basic_block_head, &inlist, &preheaders);

	            // free the memory
	    free_cfg(basic_block_head);
	    instr_head = NULL;
	    basic_block_head = NULL;

            // re-number the instructions
	    number_instrs(&instr_head, inlist);

            // re-calculate the basic blocks
	    blockCount = find_basic_block (&basic_block_head, instr_head);
	    find_predecessors(basic_block_head);

	    find_dominators(basic_block_head, blockCount);

	    loop_count = find_loop_info(basic_block_head, &loop_head, blockCount);
	    
        do {         
            print_blocks(basic_block_head);
            rerun = do_loop_invariant_code_motion(loop_head, &basic_block_head, &instr_head);
	    
	    //free_cfg(basic_block_head);
	    instr_head = NULL;
	    basic_block_head = NULL;

            // re-number the instructions
	    number_instrs(&instr_head, inlist);

            // re-calculate the basic blocks
	    blockCount = find_basic_block (&basic_block_head, instr_head);
	    
	    print_blocks(basic_block_head);
        } while(rerun);    
    }
    //printf("\n\t%s\n", proc_name);
    //print_sinstrs(*headofinstr);

    //do_other_loop_optimizations(loop_head);
    
#endif
    return *headofinstr;
}